﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
using BrainTechLLC;
using BrainTechLLC.FilterExtensions;
using BrainTechLLC.ThreadSafeObjects;
using BrainTechLLC.WPF;
using ImageAppDataModels;
using ImageAppViewModels;

namespace ImageAppViewModels
{
    public class BackgroundCalcHandler
    {
        public static ThreadSafeCounter Counter = new ThreadSafeCounter();
        public static ThreadSafeCounter CurrentBlurs = new ThreadSafeCounter();
        private FileImageViewModel _vmForBlur;
        private FileImageViewModel _vmForCompare;
        private FileImageViewModel _vmOtherForCompare;
        private ThreadStart _threadStart;
        private Thread _thread;
        private bool _started;
        private bool _wantAbort;
        private object _lock = new object();
        protected bool _threadStopAfterBlurCalc;
        protected DispatcherTimer _dt;

        public BackgroundCalcHandler(bool threadStopAfterBlurCalc)
        {
            _threadStopAfterBlurCalc = threadStopAfterBlurCalc;
            _dt = new DispatcherTimer()
            {
                Interval = new TimeSpan(0, 0, 0, 1, 0)
            };
            _dt.Tick += new EventHandler(_dt_Tick);
        }

        void _dt_Tick(object sender, EventArgs e)
        {
            _dt.Stop();
            CheckInit();
        }

        public void Abort()
        {
            _wantAbort = true;
        }

        private void CheckInit()
        {
            if (!_started)
            {
                lock (_lock)
                {
                    if (!_started)
                    {
                        if (_vmForBlur != null || _vmForCompare != null || _vmOtherForCompare != null)
                        {
                            Debug.WriteLine("Starting Background calc thread");
                            _threadStart = new ThreadStart(HandleBackgroundCalcs);
                            _wantAbort = false;
                            _thread = _threadStart.CreateAndRunThread(true);
                            _thread.Priority = ThreadPriority.BelowNormal;
                            _started = true;
                            Counter.Increment();
                        }
                    }
                }
            }
        }

        public void SetBackgroundCalcCompare(FileImageViewModel vm, FileImageViewModel vmOther)
        {
            _wantAbort = false;
            _vmForCompare = vm;
            _vmOtherForCompare = vmOther;
            _dt.Start();
        }

        public void SetBackgroundCalcBlur(FileImageViewModel vm)
        {
            _wantAbort = false;
            _vmForBlur = vm;
            _dt.Start();
        }

        public void HandleBackgroundCalcs()
        {
            Dispatcher dispatcher = Application.Current.Dispatcher;

            try
            {
                while (!_wantAbort)
                {
                    while (_vmForBlur == null && _vmForCompare == null && !_wantAbort)
                        Thread.Sleep(70);

                    if (!_wantAbort)
                    {
                        if (_vmForBlur != null)
                        {
                            DoBlurCalc(dispatcher);

                            if (_wantAbort)
                                break;

                            if (_threadStopAfterBlurCalc)
                                break;
                        }

                        if (_vmForCompare != null)
                        {
                            DoCompareCalc(dispatcher);

                            if (_wantAbort)
                                break;
                        }
                    }

                    if (_wantAbort)
                        break;

                    Thread.Sleep(30);
                }
            }
            finally
            {
                Counter.Decrement();
                _vmForBlur = null;
                _vmForCompare = null;
                _vmOtherForCompare = null;
                _thread = null;
                _threadStart = null;
                _started = false;
                _dt.Tick -= _dt_Tick;
                _dt.Stop();
                _dt = null;
            }
        }

        private void DoCompareCalc(Dispatcher dispatcher)
        {
            bool waitForMe = false;
            var vmLocal = _vmForCompare;
            var vmLocalOther = _vmOtherForCompare;
            double d;

            Thread.Sleep(150);

            if (!_wantAbort && _vmForCompare != null && vmLocal != null && _vmForCompare.FileName == vmLocal.FileName)
            {
                d = HandleComparisonCalc(vmLocal, vmLocalOther);

                if (d != double.MaxValue && !double.IsNaN(d))
                {
                    waitForMe = true;

                    dispatcher.RunOnDispatcher(new TwoParamDelegate((o1, o2) =>
                    {
                        double dbl = (double)o1;
                        FileImageViewModel vm = (FileImageViewModel)o2;

                        if (vm != null)
                        {
                            vm.SetSimilarityWith(vmLocalOther.FileImage as FileImage, dbl);
                        }

                        waitForMe = false;
                    }),
                    System.Windows.Threading.DispatcherPriority.Background, d, vmLocal);
                }
                else
                {
                    waitForMe = false;
                }
            }
            else
            {
                waitForMe = false;
            }

            while (waitForMe)
            {
                if (_wantAbort)
                    break;

                Thread.Sleep(15);
            }

            if (_vmForCompare != null && vmLocal != null && _vmForCompare.FileName == vmLocal.FileName)
            {
                _vmForCompare = null;
            }
        }

        private void DoBlurCalc(Dispatcher dispatcher)
        {
            bool waitForMe = false;

            CurrentBlurs.Increment();

            var vmLocal = _vmForBlur;

            if (vmLocal != null)
            {
                double d = vmLocal.Blur;

                if (d == double.MaxValue || double.IsNaN(d) || d == 0D)
                {
                    Thread.Sleep(1000);

                    if (!_wantAbort && _vmForBlur != null && vmLocal != null && _vmForBlur.FileName == vmLocal.FileName)
                    {
                        d = HandleBlurCalc(vmLocal);

                        if (d != double.MaxValue && !double.IsNaN(d))
                        {
                            waitForMe = true;

                            dispatcher.RunOnDispatcher(new TwoParamDelegate((o1, o2) =>
                            {
                                double dbl = (double)o1;
                                FileImageViewModel vm = (FileImageViewModel)o2;

                                if (vm != null)
                                {
                                    vm.Blur = dbl;
                                }

                                waitForMe = false;
                            }), System.Windows.Threading.DispatcherPriority.ApplicationIdle, d, vmLocal);
                        }
                        else
                        {
                            waitForMe = false;
                        }
                    }
                    else
                    {
                        waitForMe = false;
                    }
                }

                while (waitForMe)
                {
                    if (_wantAbort)
                        break;

                    Thread.Sleep(15);
                }
            }

            if (_vmForBlur != null && vmLocal != null && _vmForBlur.FileName == vmLocal.FileName)
            {
                _vmForBlur = null;
            }

            CurrentBlurs.Decrement();
        }

        private static double HandleComparisonCalc(FileImageViewModel vmLocal, FileImageViewModel vmLocalOther)
        {
            vmLocal.CurrentOther = vmLocalOther.FileImage.FileName;
            FileImage fi1 = new FileImage(vmLocal.FileName, null);
            FileImage fi2 = new FileImage(vmLocalOther.FileName, null);
            double d = fi1.Similarity(fi2, true);
            if (SimilarityQueueAndCache.SimilarityCache.Count > 4)
            {
                SimilarityQueueAndCache.SimilarityCache.Clear();
                SimilarityQueueAndCache.SimilarityQueue.Clear();
            }
            Debug.WriteLine(d);
            fi1.DetachEvents();
            fi2.DetachEvents();

            // added this release - if it breaks, remove it and re-assess
            fi1.ForceRelease(ReleaseOptions.All);
            fi2.ForceRelease(ReleaseOptions.All);

            fi1 = null;
            fi2 = null;
            return d;
        }

        private double HandleBlurCalc(FileImageViewModel vmLocal)
        {
            FileImage fi = new FileImage(vmLocal.FileName, null);
            fi.SetImageBytesSilent(vmLocal.Item.ImageBytesCached);
            // this does not work because the object may be in use elsewhere
            // fi.BitmapCached = vmLocal.FileImage.BitmapCached;
            double d = fi.GetBlur(true);
            Debug.WriteLine(d);
            fi.DetachEvents();
            fi.ForceRelease(ReleaseOptions.All);
            fi = null;
            return d;
        }
    }
}
